<template>
    <div class="h-full" :class="{ 'p-4': !appStore.isMobile }">
        <div class="dialog-window">
            <div class="window-header">
                <photos
                    width="40px"
                    height="40px"
                    radius="50%"
                    :src="robot.logo"
                    class="mr-2.5"
                />
                <div>
                    <div class="text-2xl line-clamp-1">{{ robot.name }}</div>
                    <div v-if="robot.intro" class="text-xs text-tx-secondary line-clamp-1">
                        {{ robot.intro }}
                    </div>
                </div>
            </div>

            <div class="flex-1 overflow-hidden">
                <el-watermark
                    class="h-full"
                    :content="winWatermark"
                    :font="{
                        color: isDark
                            ? 'rgba(256,256,256,0.08)'
                            : 'rgba(0,0,0,0.06)',
                        fontSize: 12
                    }"
                >
                    <el-scrollbar ref="scrollbarRef" height="100%">
                        <div ref="innerRef" class="px-6">
                            <chatting-message
                                v-if="robot.welcome_text"
                                type="answer"
                                :recordId="0"
                                :avatar="robot.logo"
                                :content="robot.welcome_text"
                                :createTime="currentTime"
                            />
                            <chatting-message
                                v-for="(item, index) in historyList"
                                :key="index"
                                :loading="item.loading"
                                :recordId="item.id"
                                :type="item.type"
                                :avatar="item.avatar"
                                :issues="item.issues"
                                :content="item.content"
                                :context="item.context"
                                :quotes="item.quotes"
                                :attach="item.attach"
                                :annex="item.annex"
                                :webSearch="item.web_search"
                                :toolsList="item.tools_list"
                                :taskTime="item.task_time"
                                :createTime="item.create_time"
                                :chatModel="item.chat_model"
                                :isShowCopy="true"
                                :isShowVoice="true"
                                :isShowShare="true"
                                :isShowiSsues="index === historyList.length - 1"
                                :isShowRewrite="index === historyList.length - 1"
                                :isShowContext="robot.is_show_context"
                                :isShowQuote="robot.is_show_quote"
                                :isWebsearch="robot.is_web_search"
                                :isContextClear="!!item.end_clear"
                                :isShowChatModel="true"
                                @rewrite="rewriteChat"
                                @search="handleSearch"
                                @stop="streamReader?.cancel()"
                                @ask="sendAsk"
                            />
                        </div>
                    </el-scrollbar>
                </el-watermark>
            </div>

            <div class="m-[15px]">
                <chatting-input-box
                    ref="chatActionRef"
                    :loading="isReceiving"
                    :instruct="robot.instruct"
                    :maxUploadNum="robot.max_upload_num"
                    :isUploadFiles="robot.is_upload_files"
                    :isUploadImage="robot.is_upload_image"
                    :isWebSearch="robot.is_web_search"
                    :isShowClear="true"
                    @search="(e: boolean) => webSearchOpen = e"
                    @clear="handleClear"
                    @sending="sendChats"
                />
            </div>
        </div>

        <el-drawer
            v-model="webSearchShow"
            :with-header="false"
        >
            <chatting-web-search
                :lists="webSearchList"
                @close="webSearchShow = false"
            />
        </el-drawer>
    </div>
</template>

<script setup lang="ts">
import { ElScrollbar } from 'element-plus'
import { useElementSize } from '@vueuse/core'
import useAppStore from '~/stores/app'
import useUserStore from '~/stores/user'
import useConfStore from '~/stores/conf'
import datetimeUtil from '~/utils/datetime'
import chatDialogApi from '~/api/agent/chat/dialog'

const props = withDefaults(
    defineProps<{
        // 身份编号
        identity?: string;
        // 访问密钥
        permits?: string;
        // 访问密码
        secrets?: string;
        // 智能体信息
        robot: ChatPublishVerifyResponse;
    }>(),
    {
        identity: '',
        permits: '',
        secrets: ''
    }
)

const appStore = useAppStore()
const userStore = useUserStore()
const confStore = useConfStore()

// 暗黑模式
const isDark = computed(() => confStore.isDarkColor)

// 窗口水印
const winWatermark = computed(() => appStore.getDialogConfig?.win_watermark)

// 节点Ref
const chatActionRef = shallowRef()
const scrollbarRef = ref<InstanceType<typeof ElScrollbar>>()
const innerRef = ref<HTMLDivElement>()

// 联网搜索
const webSearchOpen = ref<boolean>(false)
const webSearchShow = ref<boolean>(false)
const webSearchList = ref<ChatRecordsWebSearch[]>([])

// 当前时间
const currentTime = datetimeUtil.datetime()
// 正在接收
const isReceiving = ref<boolean>(false)
// 流式对象
let streamReader: ReadableStreamDefaultReader<Uint8Array> | null = null
// 历史记录
const historyList = ref<ChatRecordsListResponse[]>([])

/**
 * 获取历史记录
 */
const queryHistoryList = async () => {
    historyList.value = await chatDialogApi.history({}, {
        Permits: props.permits,
        Secrets: props.secrets,
        Identity: props.identity
    })
}

/**
 * 联网搜索显示
 */
const handleSearch = (data: any[]) => {
    webSearchShow.value = !webSearchShow.value
    webSearchList.value = data
}

/**
 * 清空对话上下文
 */
const handleClear = async () => {
    if (isReceiving.value) {
        feedback.msgError('请耐心等待回复完毕再操作!')
    } else {
        feedback.confirm('确定要清空当前对话记录吗?')
            .then(async () => {
                await chatDialogApi.clear({}, {
                    Permits: props.permits,
                    Secrets: props.secrets,
                    Identity: props.identity
                })

                const chatNum = historyList.value.length
                if (chatNum > 0) {
                    historyList.value[chatNum - 1].end_clear = 1
                }
                feedback.msgSuccess('历史对话已清空!')
                await nextTick(() => {
                    scrollToBottom()
                })
            }).catch(() => {})
    }
}

/**
 * 重新回答问题
 */
const { lockFn: rewriteChat } = useLockFn(async () => {
    const last = historyList.value[historyList.value.length - 1]
    const userInput = historyList.value.find(({ id }) => id === last.id)
    if (userInput) {
        historyList.value.splice(historyList.value.length - 2, 2)
        await sendChats(
            userInput.content,
            [{'role': 'user', 'content': userInput.content}],
            userInput.annex || []
        )
    }
})

/**
 * 发起建议提问
 */
const sendAsk = async (question: string) => {
    await sendChats(
        question,
        [{'role': 'user', 'content': question}],
        []
    )
}

/**
 * 发送流式对话
 */
const sendChats = async (question: string, messages: any[], files: any[]) => {
    if (isReceiving.value) {
        feedback.msgWarning('请等回复后再发送哦~')
        return
    }

    const datetime = datetimeUtil.datetime()
    historyList.value.push({
        id: 0,
        type: 'question',
        avatar: userStore.users.avatar,
        content: question,
        create_time: datetime,
        annex: files
    })

    historyList.value.push({
        id: 0,
        type: 'answer',
        avatar: props.robot.logo,
        thinks: '',
        content: '',
        web_search: [],
        tools_list: [],
        create_time: datetime,
        loading: true
    })

    isReceiving.value = true
    try {
        await chatDialogApi.sendChat({
            stream: true,
            search: webSearchOpen.value,
            messages: messages
        }, {
            onstart(reader) {
                chatActionRef.value?.setInputValue()
                streamReader = reader
            },
            onmessage(value) {
                value.trim().split('data:').forEach(async (text) => {
                    if (text !== '' && text !== '[DONE]') {
                        try {
                            const dataJson = JSON.parse(text)
                            if (dataJson.error) {
                                feedback.msgError(dataJson.error.message)
                            }

                            const { id, event, choices } = dataJson
                            const finish = choices[0]?.finish_reason
                            const answer = choices[0]?.delta.content
                            const thinks = choices[0]?.delta.reasoning_content
                            const toolCalls = choices[0]?.delta.tool_calls

                            let chatIndex = historyList.value.findIndex((item: any) => item.id === id)

                            if (chatIndex === -1) {
                                chatIndex = historyList.value.length - 1
                                if (id) {
                                    historyList.value[chatIndex].id = id
                                }
                            }

                            if (thinks) {
                                historyList.value[chatIndex].thinks += thinks
                            }

                            switch (event) {
                                case 'chat':
                                    if (answer) {
                                        historyList.value[chatIndex].content += answer
                                    }
                                    break
                                case 'tools':
                                    toolCalls.forEach((t: ChatRecordsToolsList) => {
                                        historyList.value[chatIndex].tools_list?.push(t)
                                    })
                                    break
                                case 'search':
                                    const search = answer ? JSON.parse(answer) : null
                                    historyList.value[chatIndex].web_search = search
                                    break
                                case 'image':
                                case 'video':
                                case 'audio':
                                case 'file':
                                    const results = JSON.parse(answer)
                                    if (Array.isArray(results)) {
                                        results.forEach((item) => {
                                            historyList.value[chatIndex].attach?.push(item)
                                        })
                                    } else {
                                        historyList.value[chatIndex].attach?.push(results)
                                    }
                                    break
                            }

                            if (finish === 'normal') {
                                historyList.value[chatIndex].loading = false
                            }
                        } catch {}
                    }
                })
            },
            onclose() {
                isReceiving.value = false
                scrollToBottom()
                setTimeout(queryHistoryList, 3000)
            }
        }, {
            Permits: props.permits,
            Secrets: props.secrets,
            Identity: props.identity
        })
    } catch {
        isReceiving.value = false
        streamReader?.cancel()
    }

    await nextTick(() => {
        scrollToBottom()
    })
}

/**
 * 滚动条到底部
 */
const scrollToBottom = () => {
    const scrollHeight: number | any = scrollbarRef.value?.wrapRef?.scrollHeight
    const clientHeight: number | any = scrollbarRef.value?.wrapRef?.clientHeight
    const scrollH = scrollHeight - clientHeight
    scrollbarRef.value?.setScrollTop(scrollH)
}

/**
 * 监听高度变化
 */
const { height } = useElementSize(innerRef)
watch(height, () => {
    if (isReceiving.value) {
        scrollToBottom()
    }
})

onMounted(async () => {
    await queryHistoryList()
    scrollToBottom()
})
</script>

<style scoped lang="scss">
.dialog-window {
    display: flex;
    flex-direction: column;
    max-width: 720px;
    height: 100%;
    margin: 0 auto;
    overflow: hidden;
    background-color: var(--el-color-light);
    border: 1px solid var(--el-border-color-lighter);
    border-radius: 10px;
    box-shadow: 0 5px 40px 0 rgb(0 0 0 / 5%);
    .window-header {
        display: flex;
        align-items: center;
        padding: 10px 15px;
        background-color: var(--color-white);
        border-bottom: 1px solid var(--el-border-color-lighter);
    }
}
</style>
